#pragma once

#include <windows.h>
#include <functional>
#include <stdio.h>
#include <string>
#include "CrossMutex.h"

namespace AsyncThread::Windows
{
	/// @brief 进程读写锁
	class ProcessReadWriteLock
	{
	public:
		ProcessReadWriteLock(const std::string uuid, const std::string lockName, int maxReader = 10, bool isGlobal = false)
			: mClassId("7256ef32-f549-4bdf-8a41-920f009c4f02"),
			mWriteLock(uuid, "write_lock", isGlobal),
			mReadLock(uuid, "read_lock", isGlobal),
			mMaxSemaphoreCount(maxReader)
		{
			mReadSemaphore = ::CreateSemaphoreA(NULL, maxReader, maxReader, MakeName(uuid, "read_semaphore", isGlobal).c_str());

			if (mReadSemaphore == NULL)
			{
				throw std::runtime_error("Failed to create read semaphore");
			}
		}

		~ProcessReadWriteLock()
		{
			if (mReadSemaphore)
				::CloseHandle(mReadSemaphore);
		}

		/// @brief 执行一个同步读
		/// @param lambda 读取逻辑
		template <
			typename TLambdaFunc,
			typename... TArgs,
			typename TResult = decltype(std::declval<TLambdaFunc>()(std::declval<TArgs>()...)),
			// TLambdaFunc 约束，必须是 class(lambda 就是一个匿名类)，返回值类型必须是 TResult
			typename = std::enable_if_t<std::is_class_v<TLambdaFunc>&& std::is_invocable_r_v<TResult, TLambdaFunc, TArgs...>>>
		TResult ReadAction(TLambdaFunc&& lambda, TArgs... args)
		{
			AutoReadLock tmp(*this);

			// 编译时分支：根据返回值类型选择执行方式
			if constexpr (std::is_void_v<TResult>)
			{
				// void 特化：无返回值
				std::forward<TLambdaFunc>(lambda)(std::forward<TArgs>(args)...);
				// 注意：此处不需要 return 语句
			}
			else
			{
				// 非 void 类型：转发返回值
				return std::forward<TLambdaFunc>(lambda)(std::forward<TArgs>(args)...);
				// 编译器会确保所有路径都有返回值（否则报错）
			}

			// 注：非 void 分支必须通过 return 返回，此处不会执行
		}

		/// @brief 执行一个同步写
		/// @param lambda 写入逻辑
		template <
			typename TLambdaFunc,
			typename... TArgs,
			typename TResult = decltype(std::declval<TLambdaFunc>()(std::declval<TArgs>()...)),
			// TLambdaFunc 约束，必须是 class(lambda 就是一个匿名类)，返回值类型必须是 TResult
			typename = std::enable_if_t<std::is_class_v<TLambdaFunc>&& std::is_invocable_r_v<TResult, TLambdaFunc, TArgs...>>>
		TResult WriteAction(TLambdaFunc&& lambda, TArgs... args)
		{
			AutoWriteLock tmp(*this);

			// 编译时分支：根据返回值类型选择执行方式
			if constexpr (std::is_void_v<TResult>)
			{
				// void 特化：无返回值
				std::forward<TLambdaFunc>(lambda)(std::forward<TArgs>(args)...);
				// 注意：此处不需要 return 语句
			}
			else
			{
				// 非 void 类型：转发返回值
				return std::forward<TLambdaFunc>(lambda)(std::forward<TArgs>(args)...);
				// 编译器会确保所有路径都有返回值（否则报错）
			}

			// 注：非 void 分支必须通过 return 返回，此处不会执行
		}

	private:

		class AutoWriteLock
		{
		public:
			explicit AutoWriteLock(ProcessReadWriteLock& lock) : m_lock(lock)
			{
				m_lock.AcquireWrite();
			}
			~AutoWriteLock()
			{
				m_lock.ReleaseWrite();
			}
			AutoWriteLock(const AutoWriteLock&) = delete;
			AutoWriteLock& operator=(const AutoWriteLock&) = delete;

		private:
			ProcessReadWriteLock& m_lock;
		};

		class AutoReadLock
		{
		public:
			explicit AutoReadLock(ProcessReadWriteLock& lock) : m_lock(lock)
			{
				m_lock.AcquireRead();
			}
			~AutoReadLock()
			{
				m_lock.ReleaseRead();
			}
			AutoReadLock(const AutoReadLock&) = delete;
			AutoReadLock& operator=(const AutoReadLock&) = delete;

		private:
			ProcessReadWriteLock& m_lock;
		};

		/// @brief 写锁定
		CrossMutex mWriteLock;

		/// @brief 读锁顶
		CrossMutex mReadLock;

		/// @brief 读者限定信号量
		HANDLE mReadSemaphore; // 读者数量控制信号量（最大10）

		/// @brief 类型 id
		const std::string& mClassId;

		long mSemaphoreCount;

		const long mMaxSemaphoreCount;

		std::string MakeName(const std::string& uniqueId, const std::string& name, bool isGlobal)
		{
			if (isGlobal)
			{
				return std::string("Global\\") + uniqueId + "_" + mClassId + "_" + name;
			}
			else
			{
				return std::string("Local\\") + uniqueId + "_" + mClassId + "_" + name;
			}
		}

		/// @brief 获取读锁
		void AcquireRead()
		{
			// 1. 优先写检查
			mWriteLock.Lock();
			mWriteLock.Unlock();

			// 2. 获取读信号量许可（若已达10则阻塞）
			WaitForSingleObject(mReadSemaphore, INFINITE);

			// 3、获取信号量剩余个数
			::ReleaseSemaphore(mReadSemaphore, 0, &mSemaphoreCount);

			// 4. 首次读取，锁定读锁
			if (mSemaphoreCount == mMaxSemaphoreCount)
			{
				// 获取读锁
				mReadLock.Lock();
			}
		}

		/// @brief 释放读锁
		void ReleaseRead()
		{
			// 1. 释放读信号量许可
			ReleaseSemaphore(mReadSemaphore, 1, &mSemaphoreCount);

			// 2. 末位读者释放写锁
			if (mSemaphoreCount == mMaxSemaphoreCount)
			{
				// 释放读锁
				mReadLock.Unlock();
			}
		}

		/// @brief 获取写锁
		void AcquireWrite()
		{
			// 1. 写锁定，其他进程禁止读取
			mWriteLock.Lock();
			// 2. 写锁顶，等待所有读进程读取完成
			mReadLock.Lock();
		}

		/// @brief 释放写锁
		void ReleaseWrite()
		{
			// 1. 释放读锁
			mReadLock.Unlock();
			// 2. 释放写锁
			mWriteLock.Unlock();
		}
	};
}